from contextlib import contextmanager
from typing import Optional, List, Set, Iterator

from pydantic import BaseModel, constr, conint, conset, ValidationError, Field, field_validator


class UserBase(BaseModel):
    last_name: str
    first_name: str
    middle_name: Optional[str]


class UserCreate(BaseModel):
    full_name: constr(
        pattern=r"(?P<first_name>[a-zA-Zа-яА-Я]+)(?P<middle_name> [a-zA-Zа-яА-Я]+)? (?P<last_name>[a-zA-Zа-яА-Я]+)"
    )
    parent_id_list: conset(conint(gt=0), max_length=2) = set()
    child_id_list: Set[conint(gt=0)] = set()


def is_recursion_validation_error(exc: ValidationError) -> bool:
    errors = exc.errors()
    return len(errors) == 1 and errors[0]['type'] == 'recursion_loop'


@contextmanager
def suppress_recursion_validation_error() -> Iterator[None]:
    try:
        yield
    except ValidationError as exc:
        if not is_recursion_validation_error(exc):
            raise exc


class UserBaseModel(UserBase):
    id: int
    children: List["UserBaseModel"] = Field(default_factory=list)

    @field_validator('children', mode='wrap')
    @classmethod
    def drop_cyclic_references(cls, children, h):
        try:
            return h(children)
        except ValidationError as exc:
            if not (
                    is_recursion_validation_error(exc)
                    and isinstance(children, list)
            ):
                raise exc

            value_without_cyclic_refs = []
            for child in children:
                with suppress_recursion_validation_error():
                    value_without_cyclic_refs.extend(h([child]))
            return h(value_without_cyclic_refs)


class User(UserBaseModel):
    parents: List["UserBaseModel"] = []
    siblings: List["UserBaseModel"] = []

    class Config:
        from_attributes = True
